﻿using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.Accounts.Services;
using mozhi.smarterp.Finance.Basedatas.Repository;
using mozhi.smarterp.Finance.Basedatas.Services;
using mozhi.smarterp.Finance.Enums;
using mozhi.smarterp.Finance.Finances.Repository;
using Mozhi.Abp.Core.Extensions;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace mozhi.smarterp.Finance.Reports
{
    public class GeneralledgerReportAppService : ReportAppService, IGeneralledgerReportAppService
    {
        public GeneralledgerReportAppService(IAccountRepository accountRepository, IDimensionGroupValueRepository dimensionGroupValueRepository, 
            ICurrencyRepository currencyRepository, ISettlementRepository settlementRepository, IAccountBalanceRepository accountBalanceRepository, 
            AccountBalanceManager accountBalanceManager, DimensionGroupValueManager dimensionGroupValueManager, IOrganizationRepository organizationRepository) 
            : base(accountRepository, dimensionGroupValueRepository, currencyRepository, settlementRepository, accountBalanceRepository, accountBalanceManager, 
                  dimensionGroupValueManager, organizationRepository)
        {
        }

        public async Task<List<GeneralledgerReportDto>> GetData(ReportQueryBase query)
        {
           var result = new List<GeneralledgerReportDto>();

            var balances = await GetAccountBalancesAsync(query);

            if (balances == null || balances.Count == 0)
                return result;

            var dtos = await ConvertToBalanceReportDto(balances);

            dtos = MergeBalanceByDimension(dtos, query);

            var groups = dtos.GroupBy(x => x.AccountId);

            foreach (IGrouping<Guid, AccountBalanceReportDto> info in groups)
            {
                var groupBalances = info.ToList();

                var generalLedgers = HandleAccountGeneralLedger(groupBalances, query.CurrencyId.HasValue);


                result.AddRange(generalLedgers);
            }

            return result;
        }


        #region
        /// <summary>
        /// 合并核算维度数据
        /// </summary>
        /// <param name="balances"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        private List<AccountBalanceReportDto> MergeBalanceByDimension(List<AccountBalanceReportDto> balances, ReportQueryBase filter)
        {
            if (balances == null || balances.Count == 0)
                return balances;

            if (filter.IncludeDimension==false)
                return balances;

            //把不是父级的核算维度都变成null
            balances.ForEach(x =>
            {
                x.DimensionGroupValueItems = x.IsLeaf==true ? x.DimensionGroupValueItems : null;
                x.DimensionGroupValueId = x.IsLeaf==true ? x.DimensionGroupValueId : null;
                x.CurrencyId = !filter.CurrencyId.HasValue ? null : x.CurrencyId;
            });

            //然后在合并父级科目的金额
            var result = new List<AccountBalanceReportDto>();


            var groups = balances.GroupBy(x => new { x.Period, x.AccountId, x.CurrencyId, UniqueValue = x.DimensionGroupValueId });

            foreach (IGrouping<object, AccountBalanceReportDto> info in groups)
            {
                var groupBalances = info.ToList();

                var balance = groupBalances.First();

                balance.OpeningAmount = groupBalances.Sum(x => x.OpeningAmount);
                balance.OpeningAmountFr = groupBalances.Sum(x => x.OpeningAmountFr);

                balance.DebitAmount = groupBalances.Sum(x => x.DebitAmount);
                balance.DebitAmountFr = groupBalances.Sum(x => x.DebitAmountFr);

                balance.CreditAmount = groupBalances.Sum(x => x.CreditAmount);
                balance.CreditAmountFr = groupBalances.Sum(x => x.CreditAmountFr);

                balance.CurrentYearDebitAmount = groupBalances.Sum(x => x.CurrentYearDebitAmount);
                balance.CurrentYearDebitAmountFr = groupBalances.Sum(x => x.CurrentYearDebitAmountFr);

                balance.CurrentYearCreditAmount = groupBalances.Sum(x => x.CurrentYearCreditAmount);
                balance.CurrentYearCreditAmountFr = groupBalances.Sum(x => x.CurrentYearCreditAmountFr);

                balance.ClosingAmount = groupBalances.Sum(x => x.ClosingAmount);
                balance.ClosingAmountFr = groupBalances.Sum(x => x.ClosingAmountFr);

                result.Add(balance);
            }

            result = result.OrderBy(x => x.Period).ThenBy(x => x.AccountCode).ToList();

            return result;
        }

        /// <summary>
        /// 处理科目总分类账数据
        /// </summary>
        /// <param name="groupBalances"></param>
        /// <param name="isBaseCurrency">是否是综合本位币</param>
        /// <returns></returns>
        private List<GeneralledgerReportDto> HandleAccountGeneralLedger(List<AccountBalanceReportDto> groupBalances, bool isBaseCurrency)
        {
            var result = new List<GeneralledgerReportDto>();

            if (groupBalances.IsNullOrEmpty())
                return result;

            groupBalances = groupBalances.OrderBy(x => x.AccountCode).ThenBy(x => x.Period).ThenBy(x => x.DimensionGroupValueId).ToList();

            var minPeriod = groupBalances.Min(x => x.Period);

            var maxPeriod = groupBalances.Max(x => x.Period);


            //然后根据最大期间和最小期间组合
            var currentPeriod = minPeriod;

            while (currentPeriod <= maxPeriod)
            {
                var periodBalances = groupBalances.Where(x => x.Period == currentPeriod).ToList();

                foreach (var balance in periodBalances)
                {
                    //一条余额要变成三条总分类账的余额
                    var generalLedgers = GetAccountGeneralLedgers(balance, currentPeriod == minPeriod, isBaseCurrency);

                    result.AddRange(generalLedgers);
                }

                currentPeriod = currentPeriod.ToNextPeriod();
            }

            if (result.Count == 0)
                return result;

            var groups = result.GroupBy(x => new { x.AccountCode, x.AccountName }).ToList();

            foreach(IGrouping<object , GeneralledgerReportDto> info in groups)
            {
                var generalLedgers = info.ToList();

                for (var i = 0; i < info.ToList().Count; i++)
                {
                    var row = generalLedgers[i];
                    if (i == 0)
                        continue;

                    row.AccountCode = "";
                    row.AccountName = "";
                }
            }

            return result;

        }

        /// <summary>
        /// 转换为总账数据
        /// </summary>
        /// <param name="balance"></param>
        /// <param name="isFirstPeriod"></param>
        /// <param name="isBaseCurrency">是否综合本位币</param>
        /// <returns></returns>
        private List<GeneralledgerReportDto> GetAccountGeneralLedgers(AccountBalanceReportDto balance, bool isFirstPeriod,bool isBaseCurrency)
        {
            var result = new List<GeneralledgerReportDto>();

            if (balance == null)
                return result;

            for (var i = 0; i < 3; i++)
            {
                //跳过期初余额行
                if (!isFirstPeriod && i == 0)
                    continue;

                var generalLedger = new GeneralledgerReportDto();

                generalLedger.AccountId = balance.AccountId;
                generalLedger.AccountCode = isFirstPeriod ? balance.AccountCode : "";
                generalLedger.AccountName = isFirstPeriod ? balance.AccountName : "";

                if (isFirstPeriod && balance.DimensionGroupValueId != null && balance.DimensionGroupValueItems != null)
                    generalLedger.AccountName = ReloadAccountName(generalLedger.AccountName , balance.DimensionGroupValueItems);

                generalLedger.IsLeaf = balance.IsLeaf;
                generalLedger.DimensionGroupValueId = balance.DimensionGroupValueId;
                generalLedger.DimensionGroupValueItems = balance.DimensionGroupValueItems;

                generalLedger.Period = balance.Period;

                generalLedger.Summary = GetSummary(i + 1);
                generalLedger.Type = i;
                generalLedger.BalanceDirection = GetBalanceDirection((int)balance.Direction);
                if (i == 0)
                {
                    generalLedger.DebitAmount = balance.Direction == AccountDirectionType.Debit ? balance.OpeningAmount : null;
                    generalLedger.DebitAmountFr = balance.Direction == AccountDirectionType.Debit ? balance.OpeningAmountFr : null;
                    generalLedger.CreditAmount = balance.Direction == AccountDirectionType.Credit ? balance.OpeningAmount : null;
                    generalLedger.CreditAmountFr = balance.Direction == AccountDirectionType.Credit ? balance.OpeningAmountFr : null;

                    generalLedger.Balance = balance.OpeningAmount.HasValue ? balance.OpeningAmount : 0;
                    generalLedger.BalanceFr = balance.OpeningAmountFr.HasValue ? balance.OpeningAmountFr : 0;
                }
                else if (i == 1)
                {
                    generalLedger.DebitAmount = balance.DebitAmount;
                    generalLedger.DebitAmountFr = balance.DebitAmountFr;
                    generalLedger.CreditAmount = balance.CreditAmount;
                    generalLedger.CreditAmountFr = balance.CreditAmount;

                    generalLedger.Balance = balance.ClosingAmount.HasValue ? balance.ClosingAmount : 0;
                    generalLedger.BalanceFr = balance.ClosingAmountFr.HasValue ? balance.ClosingAmountFr : 0;
                }
                else if (i == 2)
                {
                    generalLedger.DebitAmount = balance.CurrentYearDebitAmount;
                    generalLedger.DebitAmountFr = balance.CurrentYearDebitAmountFr;
                    generalLedger.CreditAmount = balance.CurrentYearCreditAmount;
                    generalLedger.CreditAmountFr = balance.CurrentYearCreditAmountFr;

                    generalLedger.Balance = balance.ClosingAmount.HasValue ? balance.ClosingAmount : 0;
                    generalLedger.BalanceFr = balance.ClosingAmountFr.HasValue ? balance.ClosingAmountFr : 0;
                }

                //如果综合本位币，余额未0就可以，如果选了币别，需要两个都为0
                generalLedger.BalanceDirection = isBaseCurrency ?
                    generalLedger.Balance == 0 ? GetBalanceDirection(0) : GetBalanceDirection((int)generalLedger.Direction)
                    : generalLedger.Balance == 0 && generalLedger.BalanceFr == 0 ?
                    GetBalanceDirection(0) : GetBalanceDirection((int)generalLedger.Direction);

                result.Add(generalLedger);

            }

            return result;
        }
        #endregion
    }
}
